From: Keir Fraser Date: Fri, 31 Oct 2008 14:02:39 +0000 (+0000) Subject: Change timer implementation to allow variable 'slop' in how late X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~14054^2~5 X-Git-Url: https://dgit.raspbian.org/%22http:/www.example.com/cgi/%22https:/%22bookmarks://%22Dat/%22http:/www.example.com/cgi/%22https:/%22bookmarks:/%22Dat?a=commitdiff_plain;h=3ac61e94cfe2532482b98002a5e2730f454feec7;p=xen.git Change timer implementation to allow variable 'slop' in how late timers are fired. The default continues to be 50us, but this can be configured on Xen's command line. Signed-off-by: Yu Ke Signed-off-by: Wei Gang Signed-off-by: Keir Fraser --- diff --git a/xen/arch/x86/hpet.c b/xen/arch/x86/hpet.c index 443d487f67..83ab2f1887 100644 --- a/xen/arch/x86/hpet.c +++ b/xen/arch/x86/hpet.c @@ -14,8 +14,6 @@ #include #include -#define STIME_MAX ((s_time_t)((uint64_t)~0ull>>1)) - #define MAX_DELTA_NS MILLISECS(10*1000) #define MIN_DELTA_NS MICROSECS(20) diff --git a/xen/common/timer.c b/xen/common/timer.c index 81630ce5ec..0f74b27d38 100644 --- a/xen/common/timer.c +++ b/xen/common/timer.c @@ -25,10 +25,12 @@ * We pull handlers off the timer list this far in future, * rather than reprogramming the time hardware. */ -#define TIMER_SLOP (50*1000) /* ns */ +static unsigned int timer_slop __read_mostly = 50000; /* 50 us */ +integer_param("timer_slop", timer_slop); struct timers { spinlock_t lock; + bool_t overflow; struct timer **heap; struct timer *list; struct timer *running; @@ -200,6 +202,7 @@ static int add_entry(struct timers *timers, struct timer *t) return rc; /* Fall back to adding to the slower linked list. */ + timers->overflow = 1; t->status = TIMER_STATUS_in_list; return add_to_list(&timers->list, t); } @@ -258,6 +261,7 @@ void set_timer(struct timer *timer, s_time_t expires) __stop_timer(timer); timer->expires = expires; + timer->expires_end = expires + timer_slop; if ( likely(timer->status != TIMER_STATUS_killed) ) __add_timer(timer); @@ -344,19 +348,30 @@ void kill_timer(struct timer *timer) } +static void execute_timer(struct timers *ts, struct timer *t) +{ + void (*fn)(void *) = t->function; + void *data = t->data; + + ts->running = t; + spin_unlock_irq(&ts->lock); + (*fn)(data); + spin_lock_irq(&ts->lock); + ts->running = NULL; +} + + static void timer_softirq_action(void) { struct timer *t, **heap, *next; struct timers *ts; - s_time_t now, deadline; - void (*fn)(void *); - void *data; + s_time_t now; ts = &this_cpu(timers); heap = ts->heap; - /* If we are using overflow linked list, try to allocate a larger heap. */ - if ( unlikely(ts->list != NULL) ) + /* If we overflowed the heap, try to allocate a larger heap. */ + if ( unlikely(ts->overflow) ) { /* old_limit == (2^n)-1; new_limit == (2^(n+4))-1 */ int old_limit = GET_HEAP_LIMIT(heap); @@ -377,7 +392,26 @@ static void timer_softirq_action(void) spin_lock_irq(&ts->lock); - /* Try to move timers from overflow linked list to more efficient heap. */ + now = NOW(); + + /* Execute ready heap timers. */ + while ( (GET_HEAP_SIZE(heap) != 0) && + ((t = heap[1])->expires_end < now) ) + { + remove_from_heap(heap, t); + t->status = TIMER_STATUS_inactive; + execute_timer(ts, t); + } + + /* Execute ready list timers. */ + while ( ((t = ts->list) != NULL) && (t->expires_end < now) ) + { + ts->list = t->list_next; + t->status = TIMER_STATUS_inactive; + execute_timer(ts, t); + } + + /* Try to move timers from linked list to more efficient heap. */ next = ts->list; ts->list = NULL; while ( unlikely((t = next) != NULL) ) @@ -387,51 +421,44 @@ static void timer_softirq_action(void) add_entry(ts, t); } - now = NOW(); - - while ( (GET_HEAP_SIZE(heap) != 0) && - ((t = heap[1])->expires < (now + TIMER_SLOP)) ) + ts->overflow = (ts->list != NULL); + if ( unlikely(ts->overflow) ) { - remove_entry(ts, t); - - ts->running = t; - - fn = t->function; - data = t->data; - - spin_unlock_irq(&ts->lock); - (*fn)(data); - spin_lock_irq(&ts->lock); + /* Find earliest deadline at head of list or top of heap. */ + this_cpu(timer_deadline) = ts->list->expires; + if ( (GET_HEAP_SIZE(heap) != 0) && + ((t = heap[1])->expires < this_cpu(timer_deadline)) ) + this_cpu(timer_deadline) = t->expires; } - - deadline = GET_HEAP_SIZE(heap) ? heap[1]->expires : 0; - - while ( unlikely((t = ts->list) != NULL) ) + else { - if ( t->expires >= (now + TIMER_SLOP) ) + /* + * Find the earliest deadline that encompasses largest number of timers + * on the heap. To do this we take timers from the heap while their + * valid deadline ranges continue to intersect. + */ + s_time_t start = 0, end = STIME_MAX; + struct timer **list_tail = &ts->list; + + while ( (GET_HEAP_SIZE(heap) != 0) && + ((t = heap[1])->expires <= end) ) { - if ( (deadline == 0) || (deadline > t->expires) ) - deadline = t->expires; - break; - } + remove_entry(ts, t); - ts->list = t->list_next; - t->status = TIMER_STATUS_inactive; + t->status = TIMER_STATUS_in_list; + t->list_next = NULL; + *list_tail = t; + list_tail = &t->list_next; - ts->running = t; - - fn = t->function; - data = t->data; + start = t->expires; + if ( end > t->expires_end ) + end = t->expires_end; + } - spin_unlock_irq(&ts->lock); - (*fn)(data); - spin_lock_irq(&ts->lock); + this_cpu(timer_deadline) = start; } - ts->running = NULL; - - this_cpu(timer_deadline) = deadline; - if ( !reprogram_timer(deadline) ) + if ( !reprogram_timer(this_cpu(timer_deadline)) ) raise_softirq(TIMER_SOFTIRQ); spin_unlock_irq(&ts->lock); diff --git a/xen/include/xen/time.h b/xen/include/xen/time.h index 31204e09a9..a74b69224a 100644 --- a/xen/include/xen/time.h +++ b/xen/include/xen/time.h @@ -52,6 +52,7 @@ struct tm gmtime(unsigned long t); #define SECONDS(_s) ((s_time_t)((_s) * 1000000000ULL)) #define MILLISECS(_ms) ((s_time_t)((_ms) * 1000000ULL)) #define MICROSECS(_us) ((s_time_t)((_us) * 1000ULL)) +#define STIME_MAX ((s_time_t)((uint64_t)~0ull>>1)) extern void update_vcpu_system_time(struct vcpu *v); extern void update_domain_wallclock_time(struct domain *d); diff --git a/xen/include/xen/timer.h b/xen/include/xen/timer.h index 6d4c0fc4fb..f3d2705d82 100644 --- a/xen/include/xen/timer.h +++ b/xen/include/xen/timer.h @@ -15,12 +15,13 @@ struct timer { /* System time expiry value (nanoseconds since boot). */ s_time_t expires; + s_time_t expires_end; /* Position in active-timer data structure. */ union { /* Timer-heap offset. */ unsigned int heap_offset; - /* Overflow linked list. */ + /* Linked list. */ struct timer *list_next; };